<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  
  <link rel="stylesheet" href="/public/asset/bootstrap.min.css">
  <link rel="stylesheet" href="/public/asset/bootstrap-icons-141.css">
  <link rel="stylesheet" href="/public/asset/style.css">
  
  <script src="/public/asset/jquery.min.js"></script>
  <script src="/public/asset/dayjs.min.js"></script>
  <script src="/public/asset/bootstrap.bundle.min.js"></script>
  <script src="/public/asset/util.js"></script>
  <script src="/public/asset/my-messages.js"></script>
  
  <title>Timeline - iPelago</title>
</head>
<body>
  <div id="root" class="container" style="max-width: 775px; min-width: 400px;"></div>

<script>
let update_count = 0;
const allIslands = new Map();

const Loading = CreateLoading();
const Alerts = CreateAlerts();
const Logs = CreateLogs();

const UpdateBtn = cc('button');
const TitleArea = {
  view: () => m('div').addClass('d-flex justify-content-start align-items-end my-5').append([
    m('div').addClass('display-6').text('Timeline'),
    m(UpdateBtn).text('update').attr({type:'button'})
      .addClass('btn btn-outline-secondary btn-sm ms-2'),
    m('a').text('publish').attr({href:'/public/my-messages.html'})
      .addClass('btn btn-outline-secondary btn-sm ms-2'),
    m('a').attr({href:'/public/home.html',title:'home'})
      .addClass('btn btn-outline-secondary btn-sm ms-2').append(
        m('i').addClass('bi bi-house-door')
    ),
  ]),
};

const MsgList = cc('ul');

MsgList.view = () => m('ul')
  .attr({id:MsgList.raw_id}).addClass('list-group list-group-flush');

MsgList.append = async (messages) => {
  for (const msg of messages) {
    const item = MsgItem(msg);
    $(MsgList.id).append(m(item));
    await item.init();
  }
};

const MoreBtn = cc('button');
const BottomAlerts = CreateAlerts();

const BottomArea = cc('p', null, [
  m(MoreBtn).text('More').addClass('btn btn-primary').attr({type:'button'}).hide(),
]);

$('#root').append([
  m(TitleArea),
  m(Logs),
  m(Loading),
  m(MsgPostArea).hide(),
  m(Alerts).addClass('my-3'),
  m(MsgList),
  m(BottomAlerts),
  m(BottomArea).addClass('text-center my-5'),
]);

init();

function init() {
  ajax({method:'GET',url:'/api/more-messages',alerts:Alerts},
      (messages) => {
        if (!messages || messages.length == 0) {
          Alerts.insert('info', '没有任何消息，可能尚未订阅小岛');
          return;
        }
        $(MsgPostArea.id).show();
        window.setTimeout(() => { $(MsgInput.id).focus(); }, 500);
        MsgList.append(messages);
        lastTime = messages[messages.length-1].Time;

        if (messages.length >= everyPage) {
          $(MoreBtn.id).show().click(() => {
            ajax({method:'GET',url:'/api/more-messages?time='+lastTime,alerts:BottomAlerts,buttonID:MoreBtn.id},
                (moreMessages) => {
                  if (!moreMessages || moreMessages.length == 0) {
                    BottomAlerts.insert('primary', '没有更多消息了');
                    $(MoreBtn.id).hide();
                    return;
                  }
                  MsgList.append(moreMessages);
                  lastTime = moreMessages[moreMessages.length-1].Time;
                });
          });
        }
      }, null, () => {
        $(Loading.id).hide();
      });

  $(UpdateBtn.id).click(() => {
    $(UpdateBtn.id).hide();
    ajax({method:'GET',url:'/api/all-islands',alerts:Alerts},
      (islands) => {
        if (!islands || islands.length == 0) {
          Alerts.insert('info', '尚未订阅任何小岛');
          return;
        }
        updateIslands(islands);
      });
  });
}

function itemID(id) {
  return `i${id}`;
}

function MsgItem(msg) {
  const ItemAlerts = CreateAlerts();
  const datetime = dayjs.unix(msg.Time).format('YYYY-MM-DD HH:mm:ss');
  const self = cc('div', itemID(msg.ID))
  self.view = () => m('li').attr({id:self.raw_id}).addClass('list-group-item d-flex justify-content-start align-items-start MsgItem mb-3').append([
    m('a').addClass('AvatarLink').append( m('img').addClass('Avatar') ),
    m('div').addClass('ms-3 flex-fill').append([
      m('p').addClass('Name'),
      m('div').addClass('Contents'),
      m('div').addClass('Datetime small text-muted text-end').text(datetime),
      m(ItemAlerts),
    ]),
  ]);

  self.init = async () => {
    if (!msg.IslandID) {
      msg.IslandID = 'My-Island-ID';
    }
    const island = await getIsland(msg.IslandID, ItemAlerts);

    let avatar = '/public/avatars/default.jpg';
    if (island.Avatar) {
      avatar = island.Avatar;
    }

    let islandPage = '/public/island.html?id='+msg.IslandID;
    if (msg.IslandID == 'My-Island-ID') {
      islandPage = '/public/my-messages.html';
    }

    $(self.id).find('.AvatarLink').attr({href:islandPage});
    $(self.id).find('.Avatar').attr({src:avatar,alt:'avatar'});
    $(self.id).find('.Name').append(
      m('a').text(island.Name).attr({href:islandPage}).addClass('text-decoration-none')
    );
    if (island.Email) {
      $(self.id).find('.Name').append([
        m('span').addClass('small text-muted').text(` (${island.Email})`),
      ]);
    }
    const contentsElem = $(self.id).find('.Contents');
    const httpLink = msg.Body.match(httpRegex);
    if (!httpLink) {
      contentsElem.text(msg.Body);
    } else {
      contentsElem.append([
        msg.Body.substring(0, httpLink.index),
        m('a').text(httpLink[0]).attr({href:httpLink[0],target:'_blank'}),
        msg.Body.substring(httpLink.index + httpLink[0].length),
      ]);
    }
  };

  return self;
}

async function getIsland(id, alerts) {
  if (allIslands.has(id)) {
    return allIslands.get(id);
  }
  try {
    const island = await getIslandByID(id);
    allIslands.set(id, island);
    return island;
  } catch (err) {
    alerts.insert('danger', err);
  }
}

function getIslandByID(id) {
  return new Promise((resolve, reject) => {
    ajax({method:'GET',url:'/api/get-island/'+id},
        (island) => {
          resolve(island);
        }, (errMsg) => {
          reject(errMsg);
        });
  });
}

async function updateIslands(islands) {
  update_count = 0;
  for (let i=0; i<islands.length; i++) {
    const island = islands[i];
    if (island.Status == 'unfollowed') {
      continue;
    }
    Logs.insert('dark', '正在更新: ' + island.Address);

    const Hour = 60 * 60;
    if (dayjs().unix() - island.Checked < 24*Hour) {
      Logs.insert('dark', `${island.Name} 距离上次更新时间未超过 24 小时，忽略本次更新。`);
      continue;
    }
    try {
      const status = await updateIsland(island.ID);
      if (status == 'alive') {
        update_count++;
        Logs.insert('success', `${island.Name} 更新成功`);
      } else if (status == 'alive-but-no-news') {
        Logs.insert('success', `${island.Name} 连接成功，但没有新消息`);
      } else {
        Logs.insert('danger', `${island.Name} status: ${island.Status}`);
      }
    } catch (error) {
      Logs.insert('danger', `${island.Name} ${error}`);
    }
  }
  Logs.clear();
  if (update_count > 0) {
    Logs.insert('primary', '更新结束，有新消息，本页面即将自动刷新。');
    window.setTimeout(() => { window.location.reload() }, 3000);
  } else {
    Alerts.insert('primary', '更新结束，没有新消息。');
  }
}

function updateIsland(id) {
  return new Promise((resolve, reject) => {
    const timeout = window.setTimeout(() => {
      reject(Error('timeout'));
    }, 10*1000);

    const body = new FormData();
    body.set('id', id);
    ajax({method:'POST',url:'/api/update-island',body:body},
        // onSuccess
        (island) => {
          resolve(island.Status);
        }, (errMsg) => {
          // onError
          reject(errMsg);
        }, () => {
          // onAlways
          window.clearTimeout(timeout);
        });
  });
}

</script>
</body>
</html>